package Java8新特性;

import org.junit.Test;

import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/*
    方法引用
         当要传递给 Lambda 体的操作,已经有实现的方法了,就可以使用方法引用
         方法引用可以看做是 Lambda 表达式深层次的表达,换句话说,方法引用就是 Lambda 表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法
         要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致
         格式:使用操作符 "::" 将类(或对象)与方法名分隔开来
         如下三种主要使用情况:
            情况1  对象::非静态方法
            情况2  类::静态方法名
            情况3  类::非静态方法

     方法引用的使用要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同,这种要求只针对于情况1和情况2
     当函数式接口方法的第一个参数是需要引用方法的调用者,并且第二个参数是需要引用方法的参数(或无参数)时,此时适用于情况3
 */
public class Lambda03 {

    /*
        情况一:  对象::非静态方法
        Consumer中的void accept(T t)
        PrintStream中的void println(T t)
        这两个方法中,都有相同的结构,即输入一个参数,没有返回值
     */
    @Test
    public void test1(){
        Consumer<String> con1 = str -> System.out.println(str);
        con1.accept("苏州大学");

        //当要传递给 Lambda 体的操作,已经有实现的方法了,就可以使用方法引用
        PrintStream ps = System.out;
        Consumer<String> con2 = ps::println;
        con2.accept("苏州大学");
    }

    /*
        情况一:  对象::非静态方法
        Supplier中的T get()
	    Employee中的String getName()
	    这两个方法中,都有相同的结构,没有输入参数,但有返回值
     */
    @Test
    public void test2(){
        Employee emp = new Employee(1001,"Tom",23,5600);
        Supplier<String> sup1 = () -> emp.getName();
        System.out.println(sup1.get());

        //当要传递给 Lambda 体的操作,已经有实现的方法了,就可以使用方法引用
        Supplier<String> sup2 = emp::getName;
        System.out.println(sup2.get());
    }

    /*
         情况二:  类 :: 静态方法
	     Comparator中的int compare(T t1,T t2)
	     Integer中的int compare(T t1,T t2)
	     这两个方法中,都有相同的结构,有两个形参,形参类型也必须相同,有返回值,且返回值类型也相同
     */
    @Test
    public void test3(){
        Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
        System.out.println(com1.compare(1,2));

        //当要传递给 Lambda 体的操作,已经有实现的方法了,就可以使用方法引用
        Comparator<Integer> com2 = Integer::compare;
        System.out.println(com2.compare(1,2));
    }

    /*
        情况二:  类 :: 静态方法
        Function中的R apply(T t)
	    Math中的Long round(Double d)
	    这两个方法中,都有相同的结构,有一个形参,形参类型也必须相同,有返回值,且返回值类型也相同
     */
    @Test
    public void test4(){
        Function<Double,Long> fun1 = d -> Math.round(d);
        System.out.println(fun1.apply(12.3));

        //当要传递给 Lambda 体的操作,已经有实现的方法了,就可以使用方法引用
        Function<Double,Long> fun2 = Math::round;
        System.out.println(fun2.apply(12.3));
    }

    /*
        情况三:  类 :: 非静态方法
        Comparator中的int compare(T t1,T t2)
        String中的int t1.compareTo(t2)
        这两个方法中,返回值相同,形参不同,但第一个参数是做为里面方法的调用者出现的,此时便可以使用方法引用
     */
    @Test
    public void test5(){
        Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
        System.out.println(com1.compare("abc","abd"));

        //这两个方法中,返回值相同,形参不同,但第一个参数是做为里面方法的调用者出现的,此时便可以使用方法引用
        Comparator<String> com2 = String::compareTo;
        System.out.println(com2.compare("abc","abd"));
    }

    /*
        情况三:  类 :: 非静态方法
        BiPredicate中的boolean test(T t1, T t2);
	    String中的boolean t1.equals(t2)
        这两个方法中,返回值相同,形参不同,但第一个参数是做为里面方法的调用者出现的,此时便可以使用方法引用
     */
    @Test
    public void test6(){
        BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2);
        System.out.println(pre1.test("abc","abc"));

        //这两个方法中,返回值相同,形参不同,但第一个参数是做为里面方法的调用者出现的,此时便可以使用方法引用
        BiPredicate<String,String> pre2 = String::equals;
        System.out.println(pre2.test("abc","abc"));
    }

    /*
        情况三:  类 :: 非静态方法
        Function中的R apply(T t)
	    Employee中的String getName();
	    这两个方法中,返回值相同,形参不同,但参数是做为里面方法的调用者出现的,此时便可以使用方法引用
     */
    @Test
    public void test7(){
        Employee emp = new Employee(1001,"Tom",23,5600);
        Function<Employee,String> fun1 = e -> e.getName();
        System.out.println(fun1.apply(emp));

        //这两个方法中,返回值相同,形参不同,但参数是做为里面方法的调用者出现的,此时便可以使用方法引用
        Function<Employee,String> fun2 = Employee::getName;
        System.out.println(fun2.apply(emp));
    }
}
